//
// Created by xu on 2024/11/19.
//
#include <stdio.h>
#include <libswresample/swresample.h>
#include <libavutil/avutil.h>
#include <libavutil/channel_layout.h>

#define ERROR_BUF(ret) \
    char errbuf[1024]; \
    av_strerror(ret, errbuf, sizeof(errbuf));


typedef struct {
    const char *filename;
    int sampleRate;
    enum AVSampleFormat sampleFmt;
    AVChannelLayout chLayout;
} ResampleAudioSpec;


void _resampleAudio(const char *inFilename, int inSampleRate, enum AVSampleFormat inSampleFmt,
                    AVChannelLayout *inChLayout,
                    const char *outFilename, int outSampleRate, enum AVSampleFormat outSampleFmt,
                    AVChannelLayout *outChLayout) {
    FILE *inFile = NULL;
    FILE *outFile = NULL;
    // 输入缓冲区
    uint8_t **inData = NULL;
    // 缓冲区大小
    int inLinesize = 0;
    // 声道数
    int inChs = inChLayout->nb_channels;
    // 一个样本的大小
    int inBytesPerSample = inChs * av_get_bytes_per_sample(inSampleFmt);
    // 缓冲区样本数量
    int inSamples = 1024;
    // 读取文件数据的大小
    int len = 0;

    // 输出缓冲区
    uint8_t **outData = NULL;
    // 缓冲区大小
    int outLinesize = 0;
    // 声道数
    int outChs = outChLayout->nb_channels;
    // 一个样本的大小
    int outBytesPerSample = outChs * av_get_bytes_per_sample(outSampleFmt);
    // 缓冲区的样本数量
    int outSamples = av_rescale_rnd(outSampleRate, inSamples, inSampleRate, AV_ROUND_UP);

    // 返回结果
    int ret;
    SwrContext *ctx = NULL;

    // 创建重采样上下文
    swr_alloc_set_opts2(&ctx, outChLayout, outSampleFmt, outSampleRate,
                        inChLayout, inSampleFmt, inSampleRate, 0, NULL);
    if (!ctx) {
        av_log(NULL, AV_LOG_INFO, "swr_alloc_set_opts2 error\n");
        goto end;
    }
    // 初始化重采样上下文
    ret = swr_init(ctx);
    if (ret < 0) {
        ERROR_BUF(ret);
        av_log(NULL, AV_LOG_INFO, "swr_init error: %s\n", errbuf);
        goto end;
    }

    // 创建输入缓冲区
    ret = av_samples_alloc_array_and_samples(
            &inData,
            &inLinesize,
            inChs,
            inSamples,
            inSampleFmt,
            1
    );
    if (ret < 0) {
        ERROR_BUF(ret);
        av_log(NULL, AV_LOG_INFO, "av_samples_alloc_array_and_samples error: %s\n", errbuf);
        goto end;
    }
    // 创建输出缓冲区
    ret = av_samples_alloc_array_and_samples(
            &outData,
            &outLinesize,
            outChs,
            outSamples,
            outSampleFmt,
            1
    );
    if (ret < 0) {
        ERROR_BUF(ret);
        av_log(NULL, AV_LOG_INFO, "av_samples_alloc_array_and_samples error: %s\n", errbuf);
        goto end;
    }
    // 打开文件
    inFile = fopen(inFilename, "rb");
    if (inFile == NULL) {
        av_log(NULL, AV_LOG_INFO, "open file error: %s\n", inFilename);
        goto end;
    }
    outFile = fopen(outFilename, "wb");
    if (outFile == NULL) {
        av_log(NULL, AV_LOG_INFO, "open file error: %s\n", outFilename);
        goto end;
    }

    // 读取文件数据
    while ((len = fread(inData[0], 1, inLinesize, inFile)) > 0) {
        // 读取的样本数量
        inSamples = len / inBytesPerSample;
        // 重采样
        ret = swr_convert(ctx, outData, outSamples, (const uint8_t **) inData, inSamples);
        if (ret < 0) {
            ERROR_BUF(ret);
            av_log(NULL, AV_LOG_INFO, "swr_convert error: %s\n", errbuf);
            goto end;
        }
        // 将转换后的数据写入到输出文件中
        fwrite(outData[0], 1, ret * outBytesPerSample, outFile);
    }

    // 检查一下输出缓冲区是否还有残留的样本（已经重采样过的，转换过的）
    while ((ret = swr_convert(ctx, outData, outSamples, NULL, 0)) > 0) {
        fwrite(outData[0], 1, ret * outBytesPerSample, outFile);
    }


    end:
    fclose(inFile);
    fclose(outFile);
    if (inData) {
        av_freep(&inData[0]);
    }
    av_freep(&inData);

    if (outData) {
        av_freep(&outData[0]);
    }
    av_freep(&outData);

    swr_free(&ctx);
}

void resampleAudio(ResampleAudioSpec *in, ResampleAudioSpec *out) {
    _resampleAudio(in->filename, in->sampleRate, in->sampleFmt, &in->chLayout,
                   out->filename, out->sampleRate, out->sampleFmt, &out->chLayout);
}

int main(int argc, char *argv[]) {
    AVChannelLayout inChLayout = AV_CHANNEL_LAYOUT_STEREO;
    AVChannelLayout outChLayout = AV_CHANNEL_LAYOUT_MONO;

    // 输入参数
    ResampleAudioSpec in;
    // 44100_s16le_2
    in.filename = "C:/Users/xu/Desktop/output.pcm";
    in.sampleFmt = AV_SAMPLE_FMT_S16;
    in.sampleRate = 44100;
    in.chLayout = inChLayout;

    // 输出参数
    ResampleAudioSpec out;
    out.filename = "C:/Users/xu/Desktop/output_48000_f32le_1.pcm";
    out.sampleFmt = AV_SAMPLE_FMT_FLT;
    out.sampleRate = 48000;
    out.chLayout = outChLayout;
    // 重采样
    resampleAudio(&in, &out);
    return 0;
}